#include "server_Mapa.h"
#include "server_Casillero.h"
#include "server_Coordenada.h"
#include "server_IOcupante.h"
#include "server_IMovible.h"
#include "server_Aliado.h"
#include "server_Tierra.h"
#include <iostream>
#include <vector>


using std::cout;
using std::endl;
using std::vector;

Mapa::Mapa() {
    tamanio = 0;
}

Mapa::Mapa(int tam) {
    tamanio = tam;
}

Mapa::~Mapa() {
    if (!cuadricula.empty()) {
        for (unsigned int i = 0; i < cuadricula.size(); i++) {
            while (!cuadricula[i].empty()) {
                Casillero* aux = (cuadricula[i].back());
                cuadricula[i].pop_back();
                delete aux;
            }
        }
    }
}

void Mapa::inicializar() {
    for (int i = 0; i < tamanio; i++) {
        vector<Casillero*> fila = vector<Casillero*>();
        for (int j = 0; j < tamanio; j++) {
            Coordenada coord = Coordenada(i, j);
            Casillero* casillero = new Casillero(coord);
            fila.push_back(casillero);
        }
        cuadricula.push_back(fila);
    }
}

bool Mapa::fueraDeRango(Coordenada nuevaCoord) {
    int x = nuevaCoord.getX();
    int y = nuevaCoord.getY();
    if (x < 0 || y < 0 || x >= tamanio || y >= tamanio)
        return true;
    return false;
}

bool Mapa::posicionBloqueada(Coordenada nuevaCoord) {
    int x = nuevaCoord.getX();
    int y = nuevaCoord.getY();
    Casillero* casillero = cuadricula[x][y];
    if ((casillero->getMaterial())->permiteDesplazamiento() == false)
        return true;
    return false;
}

bool Mapa::moverElemento(IMovible* movible) {
    Coordenada coordAnterior = movible->getPosicion();
    movible->mover();
    Coordenada coord = movible->getPosicion();
    /* Validar Movimiento: 
     * 1) Veo que sea una coordenada valida (que no sea negativa ni sea mas grande que el mapa).*/
    /* 2) Veo que sea un casillero donde se pueda posicionar, es decir, que no haya ni metal ni caja.*/
    if ((fueraDeRango(coord)) || (posicionBloqueada(coord))) {
        movible->retroceder();
        return false;
    }
    this->ubicarElemento(movible, coord);
    this->sacarElemento(movible, coordAnterior);
    return true;
}

bool Mapa::hayColision(Coordenada coord) {
    int x = coord.getX();
    int y = coord.getY();
    Casillero* casillero = cuadricula[x][y];
    if (casillero->estaVacio())
        return false;
    if (casillero->getOcupantes().size() == 1)
        return false;
    return true;
}

void Mapa::resolverColisiones(IMovible* movible) {
    Coordenada coord = movible->getPosicion();
    int x = coord.getX();
    int y = coord.getY();
    Casillero* casillero = cuadricula[x][y];
    if (casillero->estaVacio()) return;
    /* Colisionar con todos los personajes que se encuentre. */
    for (unsigned int i = 0; i < (casillero->getOcupantes()).size(); i++) {
        //hace que movible colisione con cada uno de los ocupantes del casillero:
        if (movible != casillero->getOcupantes()[i])
            movible->colisionarCon((casillero->getOcupantes())[i]);
    }
}

void Mapa::establecerMaterial(MaterialFisico* material, Coordenada coord) {
    int x = coord.getX();
    int y = coord.getY();
    cuadricula[x][y]->setMaterial(material);
}

void Mapa::ubicarElemento(IOcupante* elemento, Coordenada coord) {
    int x = coord.getX();
    int y = coord.getY();
    cuadricula[x][y]->agregarOcupante(elemento);
}

void Mapa::sacarElemento(IOcupante* elemento, Coordenada coord) {
    int x = coord.getX();
    int y = coord.getY();
    cuadricula[x][y]->sacarOcupante(elemento);
}

IOcupante* Mapa::obtenerOcupante(Coordenada coord) {
    int x = coord.getX();
    int y = coord.getY();
    try {
        return cuadricula[x][y]->getOcupantes()[0];
    } catch (const char* e) {
        return NULL;
    }
}

MaterialFisico* Mapa::obtenerMaterial(Coordenada coord) {
    int x = coord.getX();
    int y = coord.getY();
    return cuadricula[x][y]->getMaterial();
}

int Mapa::getTamanio() {
    return tamanio;
}

string Mapa::generarTileMap() {
    string tileMap;
    for (int i = 0; i < tamanio; i++) {
        for (int j = 0; j < tamanio; j++) {
            if ((cuadricula[i][j]->getMaterial()->serializar() == string("*"))) {
                tileMap.append(cuadricula[i][j]->getMaterial()->serializar());
            } else if (cuadricula[i][j]->estaVacio()) {
                tileMap.append(cuadricula[i][j]->getMaterial()->serializar());
            } else if (cuadricula[i][j]->getMaterial()->serializar() == string("1")) {
                tileMap.append(cuadricula[i][j]->getMaterial()->serializar());
            } else {
                vector<IOcupante*> ocupantes = cuadricula[i][j]->getOcupantes();
                tileMap.append((ocupantes[0])->serializar());
            }
        }
    }
    return tileMap;
}

void Mapa::agregarExplosion(Coordenada coordExplosion) {
    Explosion* explosion = new Explosion(coordExplosion);
    this->establecerMaterial(explosion, coordExplosion);
    this->explosiones.push_back(explosion);
}

void Mapa::limpiarExplosiones() {
    if (explosiones.empty()) return;
    list<Explosion*>::iterator iter = explosiones.begin();
    while (iter != explosiones.end()) {
        Explosion* ex = (*iter);
        if (!ex->termino()) {
            iter++;
            continue;
        }
        this->establecerMaterial(new Tierra(), ex->getPosicion());
        iter = explosiones.erase(iter);
        delete ex;
    }
}

